package com.sxt.service.impl;

import com.sxt.entity.SysMenu;
import com.sxt.entity.SysRoleMenu;
import com.sxt.entity.SysUserRole;
import com.sxt.mapper.SysMenuMapper;
import com.sxt.mapper.SysRoleMenuMapper;
import com.sxt.mapper.SysUserRoleMapper;
import com.sxt.service.SysMenuService;
import com.sxt.vo.Authority;
import com.sxt.vo.Menu;

import lombok.extern.slf4j.Slf4j;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * <p>
 * 菜单管理 服务实现类
 * </p>
 *
 * @author liangtiandong
 * @since 2019-12-17
 */
@Service
@Slf4j
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

	@Autowired
	private SysMenuMapper sysMenuMapper;

	@Autowired
	private SysUserRoleMapper sysUserRoleMapper ;

	@Autowired
	private SysRoleMenuMapper sysRoleMenuMapper;

	/**
	 * 通过用户的id 查询用户的菜单
	 * userId->roleIds->menuIds->List<Menu>
	 */
	@Override
	public List<Menu> findMenusByUser(Long userId) {
		Assert.notNull(userId, "用户的id不能为null");
		log.info("查询用户{}的菜单", userId);
		List<Object> menuIds = getMenuIdsByUser(userId);
		if(menuIds==null || menuIds.isEmpty()) {
			return Collections.emptyList() ;
		}
		List<SysMenu> sysMenus = sysMenuMapper.selectList(
				new LambdaQueryWrapper<SysMenu>().
				in(SysMenu::getMenuId, menuIds).
				isNotNull(SysMenu::getUrl)   // 菜单的url 地址不能为null
				);

		List<Menu> menus = transSysMenu(sysMenus);
		return menus;
	}

	private List<Object> getMenuIdsByUser(Long userId) {
		// 用户的id 查询用户的角色
		List<Object> roleIds = sysUserRoleMapper.selectObjs(new LambdaQueryWrapper<SysUserRole>().
				select(SysUserRole::getRoleId).
				eq(SysUserRole::getUserId, userId));
		if(roleIds==null || roleIds.isEmpty()) {
			return Collections.emptyList();
		}
		// 使用角色的id 查询菜单Id select menuId where sys_menu_role where role_id in (roleIds) ;
		List<Object> menuIds = sysRoleMenuMapper.selectObjs(new LambdaQueryWrapper<SysRoleMenu>().
				select(SysRoleMenu::getMenuId).
				in(SysRoleMenu::getRoleId, roleIds)
				);
		// menuIds 不仅仅保存菜单，还包含权限
		if(menuIds==null || menuIds.isEmpty()) {
			return Collections.emptyList();
		}
		return menuIds;
	}

	/**
	 * 得到一个树形的菜单
	 * @param sysMenus
	 * @return
	 */
	private List<Menu> transSysMenu(List<SysMenu> sysMenus) {
		// 使用2 层循环得到一个菜单
		List<Menu> rootMenu = new ArrayList<Menu>();
		for (SysMenu sysMenu : sysMenus) {
			if(sysMenu.getParentId()==0L) { // 是一个一级菜单
				rootMenu.add(sysMenu2Menu(sysMenu));
			}
		}

		// 给一级菜单里面把子菜单添加进去
		for (Menu menu : rootMenu) { 
			for (SysMenu sysMenu : sysMenus) {
				if(sysMenu.getParentId().equals(menu.getMenuId())) { // 若sysMenu 父亲 == menu.getMenuId()
					menu.getList().add(sysMenu2Menu(sysMenu));
				}
			}
		}
		return rootMenu;
	}

	/**
	 * 将SysMenu 转化为Menu
	 * @param sysMenu
	 * @return
	 */
	private Menu sysMenu2Menu(SysMenu sysMenu) {
		Menu menu = new Menu();
		menu.setMenuId(sysMenu.getMenuId());
		menu.setName(sysMenu.getName());
		menu.setUrl(sysMenu.getUrl());
		return menu;
	}

	/**
	 * 查询权限
	 */
	@Override
	public List<Authority> findAuthByUser(Long userId) {
		// userId->roleIds->MenuIds-> 权限
		List<Object> menuIds = getMenuIdsByUser(userId);
		if(menuIds == null || menuIds.isEmpty()) {
			return Collections.emptyList() ;
		}
		List<Object> perms = sysMenuMapper.selectObjs(
				new LambdaQueryWrapper<SysMenu>().
				select(SysMenu::getPerms).
				in(SysMenu::getMenuId, menuIds).
				isNotNull(SysMenu::getPerms));
		if(perms ==null || perms.isEmpty()) {
			return Collections.emptyList() ;
		}
		Set<Authority> authoritiesSet = new HashSet<Authority>(); 
		for (Object perm : perms) {
			String authoritStr = String.valueOf(perm); // 包含：sys:schedule:log 还有sys:user:save,sys:role:list
			String[] authoritStrArray = authoritStr.split(",");
			for (String eachAuthority : authoritStrArray) { // 
				Authority authority = new Authority(eachAuthority); // 将所有的权限都放进去
				authoritiesSet.add(authority);
			}
		}
		return new ArrayList<Authority>(authoritiesSet);
	}

	@Override
	public List<SysMenu> listNoButton() {
		return sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>().ne(SysMenu::getType, 2));
	}

	/**
	 * 新增一个菜单
	 */
	@Override
	public boolean save(SysMenu entity) {
		Assert.notNull(entity, "菜单不能为空");
		if(!StringUtils.hasText(entity.getName()) || entity.getType()==null) {
			throw new IllegalArgumentException("菜单的名称和类型不能为空");
		}
		validateMenu(entity); // 校验数据
		return super.save(entity);
	}

	private void validateMenu(SysMenu entity) {
		switch (entity.getType()) {
		case 0: // 代表新增的是目录
			entity.setParentId(0L); // 目录的父亲必须为0
			break;
		case 1: // 代表新增的是菜单
			if(entity.getParentId()==null || entity.getParentId()==0L) {
				throw new IllegalArgumentException("父亲不能为空");
			}

			if(!StringUtils.hasText(entity.getUrl())) {
				throw new IllegalArgumentException("菜单类型的url地址不能为空");
			}
			break;
		case 2:  // 代表新增的是按钮
			if(entity.getParentId()==null || entity.getParentId()==0L) {
				throw new IllegalArgumentException("父亲不能为空");
			}

			if(!StringUtils.hasText(entity.getPerms())) {
				throw new IllegalArgumentException("按钮的权限不能为空");
			}
			break;

		default:
			throw new IllegalArgumentException("不支持的菜单类型");
		}
	}
  
	@Override
	public boolean updateById(SysMenu entity) {
		Assert.notNull(entity, "要修改的数据不能为空");
		validateMenu(entity);
		
		// 修改之前，要保证之前的数据不能变形
		// 1 type 2 父亲
		SysMenu sysMenuDb = getById(entity.getMenuId());
		if(sysMenuDb==null) {
			throw new RuntimeException("要修改的数据不存在");
		}
		if(!entity.getType().equals(sysMenuDb.getType()) || !entity.getParentId().equals(sysMenuDb.getParentId())) {
			throw new IllegalArgumentException("要修改的数据存在结构错误");
		}
		
		return super.updateById(entity);
	}
	
	@Override
	public boolean removeById(Serializable id) {
		Assert.notNull(id, "删除时id不能为null");
		log.info("删除id为{}的数据",id);
		Integer count = sysMenuMapper.selectCount(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, id));
		if(count>0) { // 它还有有儿子
			throw new IllegalArgumentException("要删除的节点存在子节点！");
		}
		return super.removeById(id);
	}
}
